﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
//using XcoAppSpaces.Contracts.Logging;

namespace TypeResolve
{
	/// <summary>
	/// Contains methods to load assemblies and search for types for:
	/// - .Net Framework
	/// - .Net Compact Framework
	/// - Silverlight
	/// </summary>
	public static class TypeUtils
	{
		//private static IXcoLogger log = XcoLoggerFactory.CreateInstance(typeof (TypeUtils));
		public static List<Assembly> CurrentAssemblies { get; set; }
		public static bool AssembliesLoaded { get; set; }

		/// <summary>
		/// Replacable assembly loader methods
		/// </summary>
		public static Func<List<Assembly>> LoadInitialAssembliesFunc = () => new List<Assembly>();
		public static Func<List<Assembly>> LoadCurrentAssembliesFunc = () => new List<Assembly>();

		static TypeUtils()
		{
			Initialize();
			CurrentAssemblies = new List<Assembly>();
		}

		public static void Initialize()
		{
			AssembliesLoaded = false;
			CurrentAssemblies = LoadInitialAssembliesFunc();
		}

		public static List<Assembly> LoadCurrentAssemblies()
		{
			if (AssembliesLoaded)
				return CurrentAssemblies;
			var assemblies = LoadCurrentAssembliesFunc();
			AssembliesLoaded = true;
			return assemblies;
		}

		public static Type SearchType(string typeName)
		{
			//log.Debug("Resolving type: " + typeName);
			var result =
				Type.GetType(typeName) ??
				SearchTypeInAssemblies(typeName, CurrentAssemblies, "current assemblies"); //??
				//SearchTypeInAssemblies(typeName, AppDomain.CurrentDomain.GetAssemblies(), "domain assemblies"); //search again in appdomain assemblies because additional assemblies may have been loaded
			//if type not found and not all assemblies have been loaded yet, try again by loading all assemblies first
			if (result == null && !AssembliesLoaded)
			{
				CurrentAssemblies = LoadCurrentAssemblies().ToList();
				return SearchType(typeName);
			}
			//if (result == null)
			//	log.Debug("Type " + typeName + " was not found!");
			return result;
		}

		private static Type SearchTypeInAssemblies(string typeName, IEnumerable<Assembly> assemblies, string listName)
		{
			//log.Debug("Type lookup in list: " + listName);
			Type result = null;
			foreach (var asm in assemblies)
			{
				result = asm.GetType(typeName);
				if (result != null)
					break;
			}
			return result;
		}





#if PocketPC
		//CF has no methods to make array types - the following extension methods are used as replacement
		public static Type MakeArrayType(this Type type, int arrayRank)
		{
			var str = new StringBuilder(type.FullName);
			str.Append("[");
			for (int i = 1; i < arrayRank; i++)
				str.Append(",");
			str.Append("]");
			return TypeUtils.SearchType(str.ToString());
		}

		public static Type MakeArrayType(this Type type)
		{
			return TypeUtils.SearchType(type.FullName + "[]");
		}
#endif
	}
}
